﻿/**
* CRL
*/
using MongoDB.Bson;
using MongoDB.Driver;
using CRL.Data.LambdaQuery;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace CRL.Mongo.MongoDBEx
{
    /// <summary>
    /// 部份扩展方法支持
    /// </summary>
    public sealed partial class MongoDBExt
    {
        internal BsonDocument[] getBsonQuery<T>(MongoDBLambdaQuery<T> query, bool isRowCount = false)
        {
            var pageIndex = query.PageIndex;
            var pageSize = query.TakeNum;
            var skip = 0;
            if (pageIndex > 1)
            {
                skip = (pageIndex - 1) * pageIndex;
            }
            if (isRowCount)
            {
                pageSize = 0;
                skip = 0;
            }

            var pipeline = new List<BsonDocument>();
            createMatch(pipeline, query);
            createLookup(pipeline, query);

            var groupInfo = createGroup<T>(pipeline, query);
            //having
            if (query.__MongoHavingCount != null)
            {
                var havingExp = query.__MongoHavingCount;
                var op = query.getBsonOp(havingExp.ExpType);
                var having = new BsonDocument("$" + op, Convert.ToInt32(havingExp.Right.Data)).ToPackage(havingExp.Left.Data.ToString());
                pipeline.Add(new BsonDocument("$match", having));
            }
            BsonDocument projection = null;
            if (isRowCount)
            {
                var projectDic = new BsonDocument();
                projectDic.Add("_id", 0);
                projection = new BsonDocument("$project", projectDic);
                pipeline.Add(projection);
                pipeline.Add(new BsonDocument("$count", "_count"));
            }
            else
            {
                if (!query.__isFullSelect)
                {
                    projection = createProject(pipeline, query, ref groupInfo);
                }
            }

            if (pageSize > 0)
            {
                if (skip > 0)
                {
                    pipeline.Add(new BsonDocument("$skip", skip));
                }
                pipeline.Add(new BsonDocument("$limit", pageSize));
            }
            if (!isRowCount)
            {
                createSort(pipeline, query, projection);
            }
            return pipeline.ToArray();
        }
        List<TResult> GetResult<TResult, TModel>(ILambdaQuery<TModel> iQuery)
        {
            var query = iQuery as MongoDBLambdaQuery<TModel>;
            var pipeline = getBsonQuery(query);
            var collection2 = GetCollection<TModel>();

            var resultAll = collection2.Aggregate<TResult>(pipeline).ToList();
            query.__RowCount = resultAll.Count;
            if (query.PageIndex > 0)
            {
                var pipeline2 = getBsonQuery(query, true);
                var resultCount = collection2.Aggregate<dynamic>(pipeline2).ToList().FirstOrDefault();
                query.__RowCount = resultCount._count;
            }
            return resultAll;
        }
        #region QueryDynamic
        public override List<dynamic> QueryDynamic(LambdaQueryBase query)
        {
            return QueryResult<dynamic>(query, null);
        }
        #endregion

        #region QueryResult


        public override List<TResult> QueryResult<TResult>(LambdaQueryBase query, NewExpression newExpression = null)
        {
            var typeDb = this.GetType();
            var method = typeDb.GetMethod(nameof(__QueryResult), BindingFlags.NonPublic | BindingFlags.Instance);
            var result = method.MakeGenericMethod(new Type[] { query.__MainType, typeof(TResult) }).Invoke(this, new object[] { query });
            return result as List<TResult>;
        }

        List<TResult> __QueryResult<TModel, TResult>(LambdaQuery<TModel> query) where TModel : class
        {
            return GetResult<TResult, TModel>(query);
        }
        #endregion

        public override List<TModel> QueryOrFromCache<TModel>(ILambdaQuery<TModel> iQuery, out string cacheKey)
        {
            cacheKey = "none";
            return GetResult<TModel, TModel>(iQuery);
        }

        public override dynamic QueryScalar<TModel>(ILambdaQuery<TModel> query)
        {
            var result = GetResult<dynamic, TModel>(query);
            if (result.Count == 0)
            {
                return null;
            }
            var first = result.First() as IDictionary<string, object>;
            var keys = first.Keys.ToList();
            return first[keys.First()];
        }
    }
}
